import { ByteArray } from "./entity"


export interface Serializer {
  serialize<T=unknown>(data:T):ByteArray
  deserialize<T=unknown>(data:ByteArray):T
}
export const defaultSerializer:Serializer = {
  serialize<T=unknown>(data:T):ByteArray{
    return JSON.stringify(data)
  },
  deserialize<T=unknown>(data:ByteArray):T{
    return JSON.parse(data)
  }
}

export type EnumClass<T> = {
  [key:string | number | symbol]: T | string | number;
}

export interface EnumConverter {
  toByteArray<TEnum=unknown>(data:unknown,enumType:EnumClass<TEnum>, throwError?: boolean):ByteArray|undefined
  toName<TEnum=unknown>(data:unknown,enumType:EnumClass<TEnum>, throwError?: boolean):string|undefined
  toValue<T>(name: unknown, enumClass: EnumClass<T>, throwError?: boolean): T | undefined;
}


export const defaultEnumConverter:EnumConverter = {
  toByteArray<TEnum=unknown>(data:unknown,enumType:EnumClass<TEnum>, throwError?: boolean):ByteArray|undefined{
    return enumToString(data,enumType,throwError)
  },
  toName<TEnum=unknown>(data:unknown,enumType:EnumClass<TEnum>, throwError?: boolean):string|undefined{
    return enumToString(data,enumType,throwError )
  },
  toValue<T>(name: unknown, enumClass: EnumClass<T>, throwError?: boolean): T | undefined {
    // Handle numeric enum value if the name is a number string
    if (!isNaN(Number(name))) {
      const numValue = Number(name);
      // Check if this number exists as a value in the enum
      for (const key in enumClass) {
        if (enumClass[key] === numValue) {
          return numValue as unknown as T;
        }
      }
    }
    
    // Handle string enum value
    if (typeof name === 'string' && name in enumClass) {
      return enumClass[name] as T;
    }
    
    if (throwError) {
      throw new Error(`Cannot convert '${name}' to enum value`);
    }
    
    return undefined;
  }
}


function enumToString<TEnum=unknown>(data:unknown,enumType: EnumClass<TEnum>,throwError?:boolean):string|undefined{
  if (data===undefined) return undefined
  const dict = (enumType as unknown as Record<string,unknown>)
  if(typeof data === 'string' && data in dict){
    return data
  }
  if(typeof data === 'number' && data in dict){
    return (enumType as unknown as Record<string,unknown>)[data] as unknown as string
  }
  const key =data?.toString()??''
  if (/^d+$/.test(key)){
    return (enumType as unknown as Record<string,unknown>)[key] as unknown as string
  }else if (key in dict){
    return key
  }
  if(throwError){
    throw new Error(`${data} is not a valid enum value of ${enumType}`)
  }
  return undefined 
}

function enumToValue<TEnum=unknown>(data:unknown,enumType: EnumClass<TEnum>,throwError?:boolean):TEnum|undefined{
  if (data===undefined) return undefined
  const dict = (enumType as unknown as Record<string,unknown>)
  if(typeof data === 'string' && data in dict){
    return dict[data] as unknown as TEnum
  }
  if(typeof data === 'number' && data in dict){
    return data as unknown as TEnum
  }
  const key =data?.toString()??''
  if (/^d+$/.test(key) && key in dict){
    return parseInt(key) as unknown as TEnum
  }else if (key in dict){
    return dict[key] as unknown as TEnum
  }
  if(throwError){
    throw new Error(`${data} is not a valid enum value of ${enumType}`)
  }
  return undefined 
}

